from coin.exchange.base.order_gateway import OrderType
from coin.exchange.uniswap_v3.kr_rest.product import UniswapProduct
from coin.exchange.uniswap_v3.order_gateway import UniswapOrderGateway
from coin.strategy.executor.aggressive.aggressive_executor import AggressiveExecutor
from coin.strategy.executor.passive.simple_mm_executor import SimpleMMExecutor
from coin.strategy.executor.util.traits import (order_logger, SpotExecutorTraits)
from coin.proto.coin_executor_pb2 import PASSIVE_EXECUTOR
import json


class UniswapV3SimpleTraits(SpotExecutorTraits):
  @order_logger
  def _submit_order(self, product, price, qty, order_side):
    if not self.og.submittable():
      self._logger.warn('Skip submission: %s %f@%f' % (product, qty, price))
      return False

    self.og.submit(product, price, qty, order_side, OrderType.LIMIT)

  @order_logger
  def _submit_pass_order(self, product, price, qty, order_side):
    if not self.og.submittable():
      self._logger.warn('Skip submission: %s %f@%f' % (product, qty, price))
      return False

    assert self._executor_type == PASSIVE_EXECUTOR
    self.og.submit(product,
                   price,
                   qty,
                   order_side,
                   OrderType.LIMIT,
                   post_only=self.config.post_only)

  def _cancel_orders_impl(self, order_id_list):
    self.og.cancel_multiple(order_id_list)

  def _submit_provide_liquidity_order(self, **kwargs):
    if not self.og.submittable():
      self._logger.warn('skip submission of mint order')
    self._logger.info('submit order')
    self.og.liquidity_pool_submit(**kwargs)

  def _submit_withdraw_liquidity_order(self, **kwargs):
    if not self.og.submittable():
      self._logger.warn('skip submission of withdraw order')
    self._logger.info('withdraw order')
    self.og.liquidity_pool_submit(**kwargs)

  def _query_nft_token_info(self, **kwargs):
    return self.og._private_client.query_nft_token_info(**kwargs)

  def _get_position(self, product):
    return self.og.get_balance_wrapped_only(product.base.currency)


class UniswapV3SimpleMMExecutor(SimpleMMExecutor):
  ProductType = UniswapProduct
  OGType = UniswapOrderGateway
  Traits = UniswapV3SimpleTraits


class SimpleProductInfoAdjuster(object):
  def floor_price(self, price):
    if price is None:
      return None
    assert price > 0, price
    adj_price = price
    assert adj_price > 0, (price, adj_price)
    return adj_price

  def ceil_price(self, price):
    if price is None:
      return None
    assert price > 0, price
    adj_price = price
    assert adj_price > 0, (price, adj_price)
    return adj_price

  def floor_qty(self, qty):
    if qty is None:
      return None
    # 0 is allowed; it happens when qty is too small.
    assert qty >= 0., (qty)
    if qty > 0:
      adj_qty = qty
    else:
      adj_qty = qty
    assert adj_qty >= 0, (qty, adj_qty)
    return adj_qty

  def ceil_qty(self, qty):
    if qty is None:
      return None
    # 0 is allowed; it happens when qty is too small.
    assert qty >= 0., (qty)
    if qty > 0:
      adj_qty = qty
    else:
      adj_qty = qty
    assert adj_qty >= 0, (qty, adj_qty)
    return adj_qty


class UniswapV3SimpleAggExecutor(AggressiveExecutor):
  ProductType = UniswapProduct
  OGType = UniswapOrderGateway
  Traits = UniswapV3SimpleTraits

  def __init__(
      self,
      config,
      order_gateway,
      *,
      logger=None,
      latency_recorder=None):
    super().__init__(config, order_gateway, logger=logger, latency_recorder=latency_recorder)
    self._product_adjuster = SimpleProductInfoAdjuster()
    self._nbatch_loser_limit = 100

  def submit_provide_liquidity_order(self, **kwargs):
    self._traits._submit_provide_liquidity_order(product=self._product, **kwargs)
  
  def submit_withdraw_liquidity_order(self, **kwargs):
    self._traits._submit_withdraw_liquidity_order(product=self._product, **kwargs)
  
  def is_holding_nft_token(self, **kwargs):
    token_ids = self._traits._query_nft_token_info(product=self._product, **kwargs)
    return len(token_ids) > 0
